package gamecomponents;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.LinkedList;
import java.util.List;

import maze.MazeSize;

/**
 * Singleton class, contains 3 leaderboards for all maze sizes.
 * 
 * @author Team Deer
 */
public class Leaderboard {

	// Singleton Instance
	private static Leaderboard singletonInstance;

	// Storagelocation

	private final static String[] filename = new String[] {
			"Files\\leaderBoardSmall.dat", "Files\\leaderBoardMedium.dat",
			"Files\\leaderBoardLarge.dat" };

	/**
	 * List of all components, each representing a post.
	 */
	private final LinkedList<LeaderboardComponent> leaderboardComponentsSmall = new LinkedList<LeaderboardComponent>();
	private final LinkedList<LeaderboardComponent> leaderboardComponentsMedium = new LinkedList<LeaderboardComponent>();
	private final LinkedList<LeaderboardComponent> leaderboardComponentsLarge = new LinkedList<LeaderboardComponent>();

	/**
	 * List of all lists of components, each representing a post.
	 */
	private final LinkedList<LinkedList<LeaderboardComponent>> leaderboardsList = new LinkedList<LinkedList<LeaderboardComponent>>();

	// Constructor and singleton getter
	private Leaderboard() {
		this.leaderboardsList.add(this.leaderboardComponentsSmall);
		this.leaderboardsList.add(this.leaderboardComponentsMedium);
		this.leaderboardsList.add(this.leaderboardComponentsLarge);
		readLeaderboardFromFile();
	}

	/**
	 * Get singleton instance.
	 * 
	 * @return the one only instance of the Leaderboard
	 */
	public synchronized static Leaderboard getInstance() {
		if (singletonInstance == null) {
			singletonInstance = new Leaderboard();
		}
		return singletonInstance;
	}

	// Methods

	/**
	 * 
	 * Reads the leaderboard from the file location.
	 */
	@SuppressWarnings("unchecked")
	private void readLeaderboardFromFile() {

		for (int i = 0; i < 3; i++) {
			try {
				final File file = new File(filename[i]);

				if (file.exists()) {
					// Load file.
					final ObjectInputStream objectInputStream = new ObjectInputStream(
							new FileInputStream(file));
					try {
						this.leaderboardsList
								.set(i,
										(LinkedList<LeaderboardComponent>) objectInputStream
												.readObject());
					} catch (final ClassNotFoundException cnfe) {
						// Something went wrong, create new empty leaderboard.
						this.leaderboardsList.set(i,
								new LinkedList<LeaderboardComponent>());
						this.writeLeaderboardToFile();
					}
				} else {
					// No leaderboard file exists. Create a new emty one.
					this.leaderboardsList.set(i,
							new LinkedList<LeaderboardComponent>());
					this.writeLeaderboardToFile();
				}
			} catch (final IOException ioe) {
				System.out.println("IO issues when reading " + filename + ":\n"
						+ ioe.getMessage());
				this.leaderboardsList.set(i,
						new LinkedList<LeaderboardComponent>());
			}
		}

	}

	/**
	 * 
	 * Writes the leaderboard to the leaderboard file.
	 */
	public void writeLeaderboardToFile() {
		for (int i = 0; i < 3; i++) {
			try {
				final ObjectOutputStream objectOutputStream = new ObjectOutputStream(
						new FileOutputStream(filename[i]));
				objectOutputStream.writeObject(this.leaderboardsList.get(i));
				objectOutputStream.flush();
				objectOutputStream.close();
			} catch (final IOException ioe) {
				new File(filename[i]).delete();
			}
		}
	}

	/**
	 * Resets the leaderboard
	 */
	public void reset() {
		for (final LinkedList<LeaderboardComponent> leaderboard : this.leaderboardsList) {
			leaderboard.clear();
		}
		this.writeLeaderboardToFile();
	}

	/**
	 * Checks if a time qualifies into the list
	 * 
	 * @param time
	 *            The time to compare.
	 * @param date
	 *            The date to compare if times equal.
	 * @return 0 if time doesn't qualifies into the list. Otherwise the place.
	 */
	public int timeQualifies(final Time time, final MazeSize mazeSize) {

		final int index = mazeSize.getLeaderboardIndex();

		if (this.leaderboardsList.get(index).size() == 0) {
			return 0;
		}
		if (this.leaderboardsList.get(index).getLast().getTime()
				.compareTo(time) < 0) {
			for (final LeaderboardComponent sbc : this.leaderboardsList
					.get(index)) {
				final int comp = sbc.getTime().compareTo(time);
				if (comp < 0) {
					return this.leaderboardsList.get(index).indexOf(sbc);
				}
			}
		}
		if (this.leaderboardsList.get(index).size() < 10) {
			return this.leaderboardsList.get(index).size();
		}
		return -1;
	}

	/**
	 * Adds a component to the leaderboard and sorts it.
	 * 
	 * @param leaderboardComponent
	 *            - the component to add
	 */
	public void addComponent(final int index,
			final LeaderboardComponent leaderboardComponent,
			final MazeSize mazeSize) {

		this.leaderboardsList.get(mazeSize.getLeaderboardIndex()).add(index,
				leaderboardComponent);
		if (this.leaderboardsList.get(mazeSize.getLeaderboardIndex()).size() > 10) {
			this.leaderboardsList.get(mazeSize.getLeaderboardIndex())
					.removeLast();
		}
	}

	/**
	 * Returns a string representation of this leaderboard.
	 * 
	 * @param mazeSize
	 *            The leaderboard to show.
	 * @return the string
	 */
	public String toString(final MazeSize mazeSize) {
		String output = "";
		for (final LeaderboardComponent s : this.leaderboardsList.get(mazeSize
				.getLeaderboardIndex())) {
			output += this.leaderboardsList.get(mazeSize.getLeaderboardIndex())
					.indexOf(s) + 1 + "; \t" + s.toFormattedString() + "\n";
		}
		return output;
	}

	/**
	 * Get the leaderboardposts.
	 * 
	 * @param mazeSize
	 *            The leaderboard to show.
	 * @return the leaderboardposts
	 */
	public List<LeaderboardComponent> getLeaderBoardComponents(
			final MazeSize mazeSize) {
		return this.leaderboardsList.get(mazeSize.getLeaderboardIndex());
	}

}
